package descentMethods;

import functionTypes.ConstantSampling;
import functionTypes.Interpolation;
import functionTypes.OldFunction;
import functionTypes.SamplingFunction;
import tasks.Boiler;
import tasks.BoilerControlSystem;
import Abstracts.AbstractControlSystem;
import Abstracts.AbstractFunction;

public class SimpleIterationDescent {
	public String task;
	public String type;
	public double epsilon = 0.0001;

	public AbstractControlSystem individual = null;
	public double individualScore;

	public double[] eachStepBestScore;

	public Boiler boiler;

	public SimpleIterationDescent(String task, String type,
			AbstractControlSystem cs) throws Exception {

		if (!(type.equals("old") || type.equals("sampling") || type
				.equals("constantSampling")|| type.equals("interpolation"))) {
			throw new Exception("Types may be: old, sampling, constantSampling, interpolation");
		}

		if (task.equals("boiler")) {
			this.task = "boiler";
			this.type = type;
			boiler = new Boiler();
			individual = new BoilerControlSystem();
			createBoiler();

			if (type.equals("old")) {
				oldBoilerSimpleIteration();
			} else if (type.equals("sampling")) {
				samplingFunctionBoilerSimpleIteration();
			} else if (type.equals("constantSampling")) {
				constantSamplingBoilerSimpleIteration();
			} else if (type.equals("interpolation")) {
				interpolationBoilerSimpleIteration();
			}

		}
	}

	public void createBoiler() {
		AbstractFunction[] functions = null;
		if (type.equals("old")) {
			functions = new OldFunction[individual.getDimensions()];
			for (int i = 0; i != functions.length; ++i) {
				functions[i] = new OldFunction();
			}
		} else if (type.equals("sampling")) {
			functions = new SamplingFunction[individual.getDimensions()];
			for (int i = 0; i != functions.length; ++i) {
				functions[i] = new SamplingFunction();
			}
		} else if (type.equals("constantSampling")) {
			functions = new ConstantSampling[individual.getDimensions()];
			for (int i = 0; i != functions.length; ++i) {
				functions[i] = new ConstantSampling();
			}
		} else if (type.equals("interpolation")) {
			functions = new Interpolation[individual.getDimensions()];
			for (int i = 0; i != functions.length; ++i) {
				functions[i] = new Interpolation();
			}
		}
		individual.setControlSystem(functions);
	}

	public void oldBoilerSimpleIteration() {
		AbstractFunction[] functions = individual.getFunctions();

		double step = functions[0].getSubFunctions()[0].getCoeffRange();

		int q = 0;

		for (double k = step; k > epsilon; k /= 2) {
			++q;
		}

		eachStepBestScore = new double[q + 1];

		double myEff = boiler.getEffectivnes(individual);
		double newEff1;
		double newEff2;
		double oldEff = myEff;

		System.out.println("Score: " + myEff);

		q = 0;

		while (step > epsilon) {
			int i = 0;
			for (; i != functions.length; ++i) {
				int j = 0;
				for (; j != functions[i].getSubFunctions().length; ++j) {
					double[] coeff = functions[i].getSubFunctions()[j]
							.getCoeff();

					for (int k = 0; k != coeff.length; ++k) {
						double range;
						if (k == 0) {
							range = 1;
						} else {
							range = functions[i].getSubFunctions()[j]
									.getCoeffRange();
						}

						coeff[k] += step;
						if (coeff[k] > range) {
							newEff2 = 0;
						} else {
							newEff2 = boiler.getEffectivnes(individual);
						}
						coeff[k] -= 2 * step;

						if (k != 0) {
							if (coeff[k] < -range) {
								newEff1 = 0;
							} else {
								newEff1 = boiler.getEffectivnes(individual);
							}
						} else {
							if (coeff[k] < 0) {
								newEff1 = 0;
							} else {
								newEff1 = boiler.getEffectivnes(individual);
							}
						}

						coeff[k] += step;

						if ((newEff1 != 0) && (newEff2 != 0)
								&& (newEff1 > newEff2)) {
							if (newEff2 < myEff) {
								coeff[k] += step;
								myEff = newEff2;
							}
						} else if ((newEff1 != 0) && (newEff2 != 0)
								&& (newEff1 < newEff2)) {
							if (newEff1 < myEff) {
								coeff[k] -= step;
								myEff = newEff1;
							}
						} else if ((newEff1 != 0) && (newEff1 < myEff)) {
							coeff[k] -= step;
							myEff = newEff1;
						} else if ((newEff2 != 0) && (newEff2 < myEff)) {
							coeff[k] += step;
							myEff = newEff2;
						}
					}
				}
			}
			if (Math.abs(oldEff - myEff) < epsilon) {
				step /= 2;
				eachStepBestScore[q] = myEff;
				++q;
			} else {
				oldEff = myEff;
			}
			System.out.print("Step = " + step);
			System.out.println(";    NewEff = " + myEff);
		}

		System.out.println("Best score: " + myEff);
	}

	public void constantSamplingBoilerSimpleIteration() {
		samplingFunctionBoilerSimpleIteration();
	}
	
	public void interpolationBoilerSimpleIteration() {
		Interpolation[] functions = (Interpolation[])individual.getFunctions();

		double step = functions[0].getSubFunctions()[0].getCoeffRange();

		int q = 0;

		for (double k = step; k > epsilon; k /= 2) {
			++q;
		}

		eachStepBestScore = new double[q + 1];

		double myEff = boiler.getEffectivnes(individual);
		double newEff1;
		double newEff2;
		int samplesCount = functions[0].getSubFunctions().length;
		double oldEff = myEff;

		System.out.println("Score: " + myEff);

		q = 0;

		while (step > epsilon) {
			int j = 0;
			for (; j != samplesCount; ++j) {
				int i = 0;
				for (; i != functions.length; ++i) {
					double[] coeff = functions[i].getSubFunctions()[j]
							.getCoeff();

					for (int k = 0; k != coeff.length; ++k) {
						double range;
						if (k == 0) {
							range = 1;
						} else {
							range = functions[i].getSubFunctions()[j]
									.getCoeffRange();
						}

						coeff[k] += step;
						if (coeff[k] > range) {
							newEff2 = 0;
						} else {
							functions[i].calcCoeff();
							newEff2 = boiler.getEffectivnes(individual);
						}
						coeff[k] -= 2 * step;

						if (k != 0) {
							if (coeff[k] < -range) {
								newEff1 = 0;
							} else {
								functions[i].calcCoeff();
								newEff1 = boiler.getEffectivnes(individual);
							}
						} else {
							if (coeff[k] < 0) {
								newEff1 = 0;
							} else {
								functions[i].calcCoeff();
								newEff1 = boiler.getEffectivnes(individual);
							}
						}

						coeff[k] += step;

						if ((newEff1 != 0) && (newEff2 != 0)
								&& (newEff1 > newEff2)) {
							if (newEff2 < myEff) {
								coeff[k] += step;
								myEff = newEff2;
							}
						} else if ((newEff1 != 0) && (newEff2 != 0)
								&& (newEff1 < newEff2)) {
							if (newEff1 < myEff) {
								coeff[k] -= step;
								myEff = newEff1;
							}
						} else if ((newEff1 != 0) && (newEff1 < myEff)) {
							coeff[k] -= step;
							myEff = newEff1;
						} else if ((newEff2 != 0) && (newEff2 < myEff)) {
							coeff[k] += step;
							myEff = newEff2;
						}
					}
				}
			}
			if (Math.abs(oldEff - myEff) < epsilon) {
				step /= 2;
				eachStepBestScore[q] = myEff;
				++q;
			} else {
				oldEff = myEff;
			}
			System.out.print("Step = " + step);
			System.out.println(";    NewEff = " + myEff);
		}

		System.out.println("Best score: " + myEff);
	}
	
	public void samplingFunctionBoilerSimpleIteration() {
		AbstractFunction[] functions = individual.getFunctions();

		double step = functions[0].getSubFunctions()[0].getCoeffRange();

		int q = 0;

		for (double k = step; k > epsilon; k /= 2) {
			++q;
		}

		eachStepBestScore = new double[q + 1];

		double myEff = boiler.getEffectivnes(individual);
		double newEff1;
		double newEff2;
		int samplesCount = functions[0].getSubFunctions().length;
		double oldEff = myEff;

		System.out.println("Score: " + myEff);

		q = 0;

		while (step > epsilon) {
			int j = 0;
			for (; j != samplesCount; ++j) {
				int i = 0;
				for (; i != functions.length; ++i) {
					double[] coeff = functions[i].getSubFunctions()[j]
							.getCoeff();

					for (int k = 0; k != coeff.length; ++k) {
						double range;
						if (k == 0) {
							range = 1;
						} else {
							range = functions[i].getSubFunctions()[j]
									.getCoeffRange();
						}

						coeff[k] += step;
						if (coeff[k] > range) {
							newEff2 = 0;
						} else {
							newEff2 = boiler.getEffectivnes(individual);
						}
						coeff[k] -= 2 * step;

						if (k != 0) {
							if (coeff[k] < -range) {
								newEff1 = 0;
							} else {
								newEff1 = boiler.getEffectivnes(individual);
							}
						} else {
							if (coeff[k] < 0) {
								newEff1 = 0;
							} else {
								newEff1 = boiler.getEffectivnes(individual);
							}
						}

						coeff[k] += step;

						if ((newEff1 != 0) && (newEff2 != 0)
								&& (newEff1 > newEff2)) {
							if (newEff2 < myEff) {
								coeff[k] += step;
								myEff = newEff2;
							}
						} else if ((newEff1 != 0) && (newEff2 != 0)
								&& (newEff1 < newEff2)) {
							if (newEff1 < myEff) {
								coeff[k] -= step;
								myEff = newEff1;
							}
						} else if ((newEff1 != 0) && (newEff1 < myEff)) {
							coeff[k] -= step;
							myEff = newEff1;
						} else if ((newEff2 != 0) && (newEff2 < myEff)) {
							coeff[k] += step;
							myEff = newEff2;
						}
					}
				}
			}
			if (Math.abs(oldEff - myEff) < epsilon) {
				step /= 2;
				eachStepBestScore[q] = myEff;
				++q;
			} else {
				oldEff = myEff;
			}
			System.out.print("Step = " + step);
			System.out.println(";    NewEff = " + myEff);
		}

		System.out.println("Best score: " + myEff);
	}
}
